﻿using System;
using System.Collections.Generic;
using MugenInjection.Delegates;
using MugenInjection.Infrastructure;
using MugenInjection.Interface;

// ReSharper disable CheckNamespace

namespace MugenInjection.Core.Components
// ReSharper restore CheckNamespace
{
    /// <summary>
    ///     Represents the dynamic converter to decorate a specific service.
    /// </summary>
    public class DecoratorDynamicConverter : IDynamicConverter
    {
        #region Fields

        /// <summary>
        /// Gets the key that indicates that context was decorated.
        /// </summary>
        public const string DecoratedKey = "~DecoratedKey~";
        private readonly CanResolveBindingDelegate _canResolveDelegate;
        private readonly Func<IBindingContext, object, object> _decorator;

        #endregion

        #region Constructors

        /// <summary>
        ///     Initializes a new instance of the <see cref="DecoratorDynamicConverter" /> class.
        /// </summary>
        public DecoratorDynamicConverter(Type serviceToDecorate, Func<IBindingContext, object, object> decorator,
                                         CanResolveBindingDelegate canResolveDelegate)
        {
            Validate.ArgumentNotNull(serviceToDecorate, "serviceToDecorate");
            Validate.ArgumentNotNull(decorator, "decorator");
            Validate.ArgumentNotNull(canResolveDelegate, "canResolveDelegate");
            _decorator = decorator;
            _canResolveDelegate = canResolveDelegate;
            ServiceToDecorate = serviceToDecorate;
            Services = new[] { serviceToDecorate };
        }

        #endregion

        #region Properties

        /// <summary>
        ///     Gets the service to decorate.
        /// </summary>
        public Type ServiceToDecorate { get; private set; }

        #endregion

        #region Implementation of IDynamicConverter

        /// <summary>
        ///     Gets the service types.
        /// </summary>
        public IList<Type> Services { get; private set; }

        /// <summary>
        ///     Determines whether the specified request can be resolved.
        /// </summary>
        /// <param name="bindingContext">
        ///     The specified <see cref="IBindingContext" />.
        /// </param>
        /// <param name="convertType">The type to convert.</param>
        /// <returns>
        ///     <c>True</c> if the specified service has been resolved; otherwise, <c>false</c>.
        /// </returns>
        public bool CanResolve(IBindingContext bindingContext, out Type convertType)
        {
            if (bindingContext.SpecialParameters != null && !bindingContext.SpecialParameters.ContainsKey(DecoratedKey) && _canResolveDelegate(bindingContext))
            {
                convertType = ServiceToDecorate;
                return true;
            }
            convertType = null;
            return false;
        }

        /// <summary>
        ///     Resolves instance for the specified <see cref="IBindingContext" />.
        /// </summary>
        /// <param name="context">
        ///     The specified <see cref="ConverterContext" />.
        /// </param>
        /// <returns>An instance of the service.</returns>
        public object Resolve(ConverterContext context)
        {
            var bindingContext = context.BindingContext;
            var dictionary = InjectorUtilsInternal.GetDictionary(bindingContext);
            dictionary[DecoratedKey] = true;
            var resolve = context.Activator(bindingContext);
            return _decorator(bindingContext, resolve);
        }

        #endregion
    }
}